package com.alibaba.citrus.cr.price.ability.functions.entity.price;

import com.alibaba.acm.shaded.com.google.common.collect.Lists;
import com.alibaba.acm.shaded.com.google.common.collect.Maps;
import com.alibaba.citrus.cr.common.exception.FacadeException;
import com.alibaba.citrus.cr.common.util.MD5Utils;
import com.alibaba.citrus.cr.common.util.SpringContextUtil;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSONObject;
import com.epoch.app.crpricecenter.dto.request.DimensionProperty;
import com.epoch.app.crpricecenter.enums.DimensionPropertyEnum;
import com.epoch.app.crpricecenter.sdo.PriceDimensionConfigSDO;
import com.epoch.app.crpricecenter.sdo.PriceDimensionSDO;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author: qiaojiange
 * @create: 2021/09/16
 * @Description 维度规则实体
 */
@Data
public class PriceDimensionRuleEntity {

    private static final Log log = Log.getLogger(PriceDimensionRuleEntity.class);

    public static final String UTF_8 = "UTF-8";

    private PriceDimensionSDO priceDimensionSDO;

    private static ExpressRunner expressRunner = SpringContextUtil.getBean(ExpressRunner.class);

    private static final String MESSAGE_ID = "messageId";

    private static final String DATA_SOURCE = "dataSource";


    public static PriceDimensionRuleEntity of(PriceDimensionSDO dimensionRuleSDO) {
        PriceDimensionRuleEntity priceDimensionRuleEntity = new PriceDimensionRuleEntity();
        priceDimensionRuleEntity.setPriceDimensionSDO(dimensionRuleSDO);
        return priceDimensionRuleEntity;
    }

    /**
     * 计算 md5字符串
     *
     * @param request
     * @return
     */
    public String calcMD5(com.epoch.app.crpricecenter.dto.request.DimensionPropertyInfoRequest request) {

//        1. 构建上下文 context；
//        2. 获取运行规则
//        3. 执行规则获取属性组字符串
//        4. 对属性组字符串进行MD5编码，并返回
        IExpressContext<String, Object> context = new DefaultContext();
        /**
         * 销售组织编码
         */
        com.epoch.app.crpricecenter.dto.request.PlaceInfoRequest placeInfoRequest = request.getPlaceInfoRequest();
        if (StringUtils.isNotBlank(placeInfoRequest.getOrgSalesOrganizationCode())) {
            context.put(DimensionPropertyEnum.ORG_SALES_ORGANIZATION_CODE.getKey(), placeInfoRequest.getOrgSalesOrganizationCode());
        }


        /**
         * 分销渠道编码（业务渠道）
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getChannelCode())) {
            context.put(DimensionPropertyEnum.CHANNEL_CODE.getKey(), placeInfoRequest.getChannelCode());
        }


        /**
         * 销售单位编码（是指箱、包、件,不要搞错了哈）
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getSalesUnitCode())) {
            context.put(DimensionPropertyEnum.SALES_UNIT_CODE.getKey(), placeInfoRequest.getSalesUnitCode());
        }


        /**
         * 销售地区编码
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getSalesArea())) {
            context.put(DimensionPropertyEnum.SALES_AREA.getKey(), placeInfoRequest.getSalesArea());
        }


        /**
         * 工厂编码（传逻辑仓库中的工厂编码）
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getFactoryCode())) {
            context.put(DimensionPropertyEnum.FACTORY_CODE.getKey(), placeInfoRequest.getFactoryCode());
        }



        /**
         * 销售部门
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getSalesDepartment())) {
            context.put(DimensionPropertyEnum.SALES_DEPARTMENT.getKey(), placeInfoRequest.getSalesDepartment());
        }



        /**
         * 销售组
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getSalesGroup())) {
            context.put(DimensionPropertyEnum.SALES_GROUP.getKey(), placeInfoRequest.getSalesGroup());
        }



        /**
         * 存储位置
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getStorePlace())) {
            context.put(DimensionPropertyEnum.STORE_PLACE.getKey(), placeInfoRequest.getStorePlace());
        }



        /**
         * 部门编码
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getDepartmentCode())) {
            context.put(DimensionPropertyEnum.DEPARTMENT.getKey(), placeInfoRequest.getDepartmentCode());
        }


        /**
         * 定价区域
         */
        if (StringUtils.isNotBlank(placeInfoRequest.getPriceArea())) {
            context.put(DimensionPropertyEnum.PRICE_AREA.getKey(), placeInfoRequest.getPriceArea());
        }



        /**
         * 送达方编码
         * 客户收货地址编码（SAP 送达方编码）
         */
        com.epoch.app.crpricecenter.dto.request.HumanInfoRequest humanInfoRequest = request.getHumanInfoRequest();
        if (StringUtils.isNotBlank(humanInfoRequest.getCustomerSendAddressCode())) {
            context.put(DimensionPropertyEnum.CUSTOMER_SEND_ADDRESS_CODE.getKey(), humanInfoRequest.getCustomerSendAddressCode());
        }


        /**
         * 售达方编码
         * 客户编码（SAP售达方编码）
         */
        if (StringUtils.isNotBlank(humanInfoRequest.getCustomerSaleAddressCode())) {
            context.put(DimensionPropertyEnum.CUSTOMER_SALE_ADDRESS_CODE.getKey(), humanInfoRequest.getCustomerSaleAddressCode());
        }


        /**
         * 客户组编码
         */
        if (StringUtils.isNotBlank(humanInfoRequest.getCustomerGroup())) {
            context.put(DimensionPropertyEnum.CUSTOMER_GROUP.getKey(), humanInfoRequest.getCustomerGroup());
        }


        /**
         * 价目清单编码 (从客户信息上取)
         */
        if (StringUtils.isNotBlank(humanInfoRequest.getPriceListCode())) {
            context.put(DimensionPropertyEnum.PRICE_LIST_CODE.getKey(), humanInfoRequest.getPriceListCode());
        }


        /**
         * 客户编码 (从客户信息上取)
         */
        if (StringUtils.isNotBlank(humanInfoRequest.getCustomerCode())) {
            context.put(DimensionPropertyEnum.CUSTOM_CODE.getKey(), humanInfoRequest.getCustomerCode());
        }


        /**
         * 货品编码（ SAP物料编码）
         */
        com.epoch.app.crpricecenter.dto.request.MaterialInfoRequest materialInfoRequest = request.getMaterialInfoRequest();
        if (StringUtils.isNotBlank(materialInfoRequest.getMaterialCode())) {
            context.put(DimensionPropertyEnum.MATERIAL_CODE.getKey(), materialInfoRequest.getMaterialCode());
        }


        /**
         * 产品组编码( 也叫货品组编码)
         */
        if (StringUtils.isNotBlank(materialInfoRequest.getScItemGroupCode())) {
            context.put(DimensionPropertyEnum.SC_ITEM_GROUP_CODE.getKey(), materialInfoRequest.getScItemGroupCode());
        }
        String rule = this.obtainRule();
        //根据规则表达式，运行计算规则，拼接字符串
        String dimensionPropertyGroup = null;
        try {
            dimensionPropertyGroup = (String) expressRunner.execute(rule, context, null, true, true);
        } catch (Exception e) {
            log.error("PriceQueryServiceImpl#batchQueryPrice_fail");
            log.error("----表达式执行异常---" + dimensionPropertyGroup, e);
        }
        return MD5Utils.encrypt32(dimensionPropertyGroup);
    }

    /**
     * 获取配置规则
     *
     * @return 规则字符串
     */
    private String obtainRule() {
        return this.getPriceDimensionSDO().getDimensionRule();
    }

    /**
     * 获取维度属性组字符串
     *
     * @param request
     * @return
     */
    public String obtainDimensionPropertiesGroup(com.epoch.app.crpricecenter.dto.request.SyncPriceDimensionConfigDataRequest request, PriceDimensionConfigSDO sdo) {
        IExpressContext<String, Object> context = new DefaultContext();
        request.getDimensionPropertyList().forEach(e -> {
            context.put(e.getDimensionName(), e.getDimensionValue());
        });

        checkDimensionProperties(request.getDimensionPropertyList());

        String rule = this.obtainRule();

        //存冗余字段
        Map<String,String> featuresMap = Maps.newHashMap();
        //冗余消息头id
        featuresMap.put(MESSAGE_ID,request.getMessageId());
        //冗余数据来源
        featuresMap.put(DATA_SOURCE,request.getDataSource());

        Map<String,String> dimensionMap = Maps.newHashMap();
       request.getDimensionPropertyList().forEach(e->dimensionMap.put(e.getDimensionName(),e.getDimensionValue()));
        //冗余编码信息用于获取描述数据,_flag用于保存原始的编码
        List<String> dimensionPropertyList = obtainDimensionPropertyList(rule);
        dimensionPropertyList.forEach(e->{
            featuresMap.put(e,dimensionMap.get(e));
            featuresMap.put(e+"_flag", dimensionMap.get(e));
        });

        sdo.setFeaturesMap(featuresMap);

        //根据规则表达式，运行计算规则，拼接字符串
        String dimensionPropertyGroup = null;
        try {
            dimensionPropertyGroup = (String) expressRunner.execute(rule, context, null, true, true);
        } catch (Exception e) {
            //todo qjg 需要确定下
            log.error("PriceQueryServiceImpl#batchQueryPrice_fail");
            log.error("----表达式执行异常---" + dimensionPropertyGroup, e);
        }
        return dimensionPropertyGroup;
    }

    /**
     * 校验维度属性
     * @param dimensionPropertyList 维度属性列表
     */
    private void checkDimensionProperties(List<com.epoch.app.crpricecenter.dto.request.DimensionProperty> dimensionPropertyList) {

        Set<String> inputDimensionNameSet = dimensionPropertyList.stream().map(com.epoch.app.crpricecenter.dto.request.DimensionProperty::getDimensionName).collect(Collectors.toSet());

        String dimensionDesc = this.getPriceDimensionSDO().getDimensionDesc();
        String[] dimensionProperties = dimensionDesc.split("\\+");

        List<String> ruleProperties  = Arrays.stream(dimensionProperties).collect(Collectors.toList());
        Set<String> rulePropertiesSet = Arrays.stream(dimensionProperties).collect(Collectors.toSet());

        ruleProperties.forEach(propertyDesc->{
            String key = obtainKey(propertyDesc);
            //判断没有包含的情况,缺少某个属性的情况
            if (!inputDimensionNameSet.contains(key)) {
                log.error("未包含" + key + "属性,请重新核查同步数据！", dimensionPropertyList);
                throw new FacadeException("未包含" + key + "属性,请重新核查同步数据！", dimensionPropertyList);
            }

            //获取维度属性名和属性值的对应关系,用于校验该属性是否传值
            Map<String, DimensionProperty> dimensionNameAndValueMap = dimensionPropertyList.stream().collect(Collectors.toMap(DimensionProperty::getDimensionName, t -> t,(n1,n2)->n2));
            if(StringUtils.isBlank(dimensionNameAndValueMap.get(key).getDimensionValue())){
                log.error("校验维度属性入参 result = {}", JSONObject.toJSONString(dimensionProperties));
                throw new FacadeException("该" + key + "属性未传值，请重新核查同步数据！");
            }


            inputDimensionNameSet.remove(key);
            rulePropertiesSet.remove(propertyDesc);
        });

        //判断是否有维度属性少传的情况
        if (!rulePropertiesSet.isEmpty()) {
            StringBuilder stringBuilder = new StringBuilder();
            boolean isFirst = true;
            for (String s : inputDimensionNameSet) {
                if (isFirst) {
                    stringBuilder.append(s);
                    isFirst = !isFirst;
                } else {
                    stringBuilder.append("," + s);
                }
            }
            throw new FacadeException("未传入 " + stringBuilder.toString() + "属性，请补充完成！", dimensionProperties);
        }
        //用户传入的维度多的情况，属于正常逻辑，不用处理
    }

    /**
     * 获取维度 id
     * @return
     */
    public String obtainDimensionId() {
        return this.getPriceDimensionSDO().getDimensionId();
    }

    private static String obtainKey(String dimensionPropertyDesc){
        return Arrays.stream(DimensionPropertyEnum.values())
                .filter(e->e.getDimensionPropertyDesc().equals(dimensionPropertyDesc))
                .findFirst().get().getKey();
    }


    /**
     * 获取维度属性数组
     * @param dimensionId
     * @return
     */
    private List<String> obtainDimensionPropertyList(String dimensionId){
        if(StringUtils.isBlank(dimensionId)){
            return new ArrayList<>();
        }
        List<String> property = Lists.newArrayList();
        for (String ruleProperty : dimensionId.split(",")) {
            String[] dimensionRuleArr = ruleProperty.split("\\+");
            property.add(dimensionRuleArr[1]);
        }
        return property;
    }
}
